home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 June / PersonalComputerWorld-June2009-CoverdiscCD.iso / Software / Freeware / Firebug 1.3.3 / firebug-1.3.3-fx.xpi / content / firebug / firebug.js < prev    next >
Encoding:
JavaScript  |  2009-02-19  |  85.1 KB  |  2,928 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. FBL.ns(function() { with (FBL) {
  4.  
  5. // ************************************************************************************************
  6. // Constants
  7.  
  8. const Cc = Components.classes;
  9. const Ci = Components.interfaces;
  10.  
  11. const nsIPrefBranch = Ci.nsIPrefBranch;
  12. const nsIPrefBranch2 = Ci.nsIPrefBranch2;
  13. const nsIFireBugClient = Ci.nsIFireBugClient;
  14. const nsISupports = Ci.nsISupports;
  15. const nsIFile = Ci.nsIFile;
  16. const nsILocalFile = Ci.nsILocalFile;
  17. const nsISafeOutputStream = Ci.nsISafeOutputStream;
  18. const nsIURI = Ci.nsIURI;
  19.  
  20. const PrefService = Cc["@mozilla.org/preferences-service;1"];
  21. const PermManager = Cc["@mozilla.org/permissionmanager;1"];
  22. const DirService =  CCSV("@mozilla.org/file/directory_service;1", "nsIDirectoryServiceProvider");
  23. const ioService = CCSV("@mozilla.org/network/io-service;1", "nsIIOService");
  24.  
  25. const nsIPrefService = Ci.nsIPrefService;
  26. const prefService = PrefService.getService(nsIPrefService);
  27.  
  28. const nsIPermissionManager = Ci.nsIPermissionManager;
  29. const permissionManager = CCSV("@mozilla.org/permissionmanager;1", "nsIPermissionManager");
  30. const observerService = CCSV("@mozilla.org/observer-service;1", "nsIObserverService");
  31.  
  32. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  33.  
  34. const contentBox = $("fbContentBox");
  35. const contentSplitter = $("fbContentSplitter");
  36. const toggleCommand = $("cmd_toggleFirebug");
  37. const detachCommand = $("cmd_toggleDetachFirebug");
  38. const tabBrowser = $("content");
  39. const fbStatusIcon = $('fbStatusIcon');
  40.  
  41.  
  42. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  43.  
  44. const prefs = PrefService.getService(nsIPrefBranch2);
  45. const pm = PermManager.getService(nsIPermissionManager);
  46.  
  47. const DENY_ACTION = nsIPermissionManager.DENY_ACTION;
  48. const ALLOW_ACTION = nsIPermissionManager.ALLOW_ACTION;
  49. const NS_OS_TEMP_DIR = "TmpD"
  50.  
  51. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  52.  
  53. const firebugURLs =
  54. {
  55.     main: "http://www.getfirebug.com",
  56.     docs: "http://www.getfirebug.com/docs.html",
  57.     keyboard: "http://www.getfirebug.com/keyboard.html",
  58.     discuss: "http://groups.google.com/group/firebug",
  59.     issues: "http://code.google.com/p/fbug/issues/list",
  60.     donate: "http://www.getfirebug.com/contribute.html?product"
  61. };
  62.  
  63. const prefNames =
  64. [
  65.     // Global
  66.     "defaultPanelName", "throttleMessages", "textSize", "showInfoTips",
  67.     "largeCommandLine", "textWrapWidth", "openInWindow", "showErrorCount",
  68.  
  69.     // Console
  70.     "showJSErrors", "showJSWarnings", "showCSSErrors", "showXMLErrors",
  71.     "showChromeErrors", "showChromeMessages", "showExternalErrors",
  72.     "showXMLHttpRequests",
  73.  
  74.     // HTML
  75.     "showFullTextNodes", "showCommentNodes", "showWhitespaceNodes",
  76.     "highlightMutations", "expandMutations", "scrollToMutations", "shadeBoxModel",
  77.  
  78.     // CSS
  79.     "showComputedStyle", "showUserAgentCSS",
  80.  
  81.     // Script
  82.     "breakOnTopLevel",
  83.  
  84.     // DOM
  85.     "showUserProps", "showUserFuncs", "showDOMProps", "showDOMFuncs", "showDOMConstants",
  86.  
  87.     // Layout
  88.     "showRulers",
  89.  
  90.     // Net
  91.     "netFilterCategory", "collectHttpHeaders",
  92.  
  93.     // Stack
  94.     "omitObjectPathStack",
  95. ];
  96.  
  97. const servicePrefNames = [
  98.     "showStackTrace", // Console
  99.     "filterSystemURLs", // Stack
  100.     "showAllSourceFiles", "breakOnErrors",  "trackThrowCatch" // Script
  101. ];
  102.  
  103. const scriptBlockSize = 20;
  104.  
  105. // ************************************************************************************************
  106. // Globals
  107.  
  108. var modules = [];
  109. var activableModules = [];
  110. var extensions = [];
  111. var uiListeners = [];
  112. var panelTypes = [];
  113. var reps = [];
  114. var defaultRep = null;
  115. var editors = [];
  116. var externalEditors = [];
  117.  
  118. var panelTypeMap = {};
  119. var optionUpdateMap = {};
  120.  
  121. var deadWindows = [];
  122. var deadWindowTimeout = 0;
  123. var clearContextTimeout = 0;
  124. var temporaryFiles = [];
  125. var temporaryDirectory = null;
  126.  
  127. // ************************************************************************************************
  128.  
  129. top.Firebug =
  130. {
  131.     version: "1.3",
  132.  
  133.     module: modules,
  134.     panelTypes: panelTypes,
  135.     reps: reps,
  136.     prefDomain: "extensions.firebug",
  137.     servicePrefDomain: "extensions.firebug-service",
  138.  
  139.     stringCropLength: 80,
  140.  
  141.     tabBrowser: tabBrowser,
  142.  
  143.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  144.     // Initialization
  145.  
  146.     initialize: function()
  147.     {
  148.         var version = this.getVersion();
  149.         if (version)
  150.         {
  151.             this.version = version;
  152.             $('fbStatusIcon').setAttribute("tooltiptext", "Firebug "+version);
  153.  
  154.             var about = $('Firebug_About');
  155.             if (about)
  156.             {
  157.                 var aboutLabel = about.getAttribute("label");
  158.                 $('Firebug_About').setAttribute("label",  aboutLabel + " " + version);
  159.             }
  160.         }
  161.  
  162.         for (var i = 0; i < prefNames.length; ++i)
  163.             this[prefNames[i]] = this.getPref(this.prefDomain, prefNames[i]);
  164.         for (var i = 0; i < servicePrefNames.length; ++i)
  165.             this[servicePrefNames[i]] = this.getPref(this.servicePrefDomain, servicePrefNames[i]);
  166.  
  167.         this.internationalizeUI();
  168.  
  169.         this.loadExternalEditors();
  170.         prefs.addObserver(this.prefDomain, this, false);
  171.         prefs.addObserver(this.servicePrefDomain, this, false);
  172.  
  173.         var basePrefNames = prefNames.length;
  174.         dispatch(modules, "initialize", [this.prefDomain, prefNames]);
  175.  
  176.         for (var i = basePrefNames; i < prefNames.length; ++i)
  177.             this[prefNames[i]] = this.getPref(this.prefDomain, prefNames[i]);
  178.  
  179.     },
  180.  
  181.     getVersion: function()
  182.     {
  183.         if (this.fullVersion)
  184.             return this.fullVersion;
  185.  
  186.         var versionURL = "chrome://firebug/content/branch.properties";
  187.         var content = getResource(versionURL);
  188.         var m = /RELEASE=(.*)/.exec(content);
  189.         if (m)
  190.         {
  191.             var release = m[1];
  192.         }
  193.         m = /VERSION=(.*)/.exec(content);
  194.         if (m)
  195.         {
  196.             var version = m[1];
  197.         }
  198.         if (release && version)
  199.         {
  200.             this.fullVersion = version+""+release;
  201.             return this.fullVersion;
  202.         }
  203.         else
  204.         {
  205.             FBTrace.sysout("firebug.getVersion fails with release="+release+" branch="+branch+" from content="+content+"\n");
  206.         }
  207.     },
  208.  
  209.     internationalizeUI: function()  // Substitute strings in the UI with fall back to en-US
  210.     {
  211.         FBL.internationalize('menu_toggleSuspendFirebug', 'label');
  212.     },
  213.  
  214.     broadcast: function(message, args)
  215.     {
  216.         // dispatch message to all XUL windows registered to firebug service.
  217.         // Implemented in Firebug.Debugger.
  218.     },
  219.     /**
  220.      * Called when the UI is ready to be initialized, once the panel browsers are loaded,
  221.      * but before any contexts are created.
  222.      */
  223.     initializeUI: function(detachArgs)
  224.     {
  225.         TabWatcher.initialize(this);
  226.  
  227.         // If another window is opened, then the creation of our first context won't
  228.         // result in calling of enable, so we have to enable our modules ourself
  229.         //if (fbs.enabled)
  230.         dispatch(modules, "enable");  // allows errors to flow thru fbs and callbacks to supportWindow to begin
  231.  
  232.         dispatch(modules, "initializeUI", [detachArgs]);
  233.         Firebug.ActivableModule.resetTooltip();  // 1.3.1 set the initial value of the tool tip
  234.     },
  235.  
  236.     shutdown: function()
  237.     {
  238.         TabWatcher.destroy();
  239.  
  240.         dispatch(modules, "disable");
  241.  
  242.         prefService.savePrefFile(null);
  243.         prefs.removeObserver(this.prefDomain, this, false);
  244.         prefs.removeObserver(this.servicePrefDomain, this, false);
  245.  
  246.         dispatch(modules, "shutdown");
  247.  
  248.         this.closeDeadWindows();
  249.         this.deleteTemporaryFiles();
  250.                                                                                                                        /*@explore*/
  251.     },
  252.  
  253.     // ----------------------------------------------------------------------------------------------------------------
  254.  
  255.     getSuspended: function()
  256.     {
  257.         var suspendMenuItem = $("menu_toggleSuspendFirebug");
  258.         if (suspendMenuItem.hasAttribute("suspended"))
  259.             return suspendMenuItem.getAttribute("suspended");
  260.         return null;
  261.     },
  262.  
  263.     setSuspended: function(value)
  264.     {
  265.         var suspendMenuItem = $("menu_toggleSuspendFirebug");
  266.         if (value)
  267.         {
  268.             suspendMenuItem.setAttribute("suspended", value);
  269.             $('menu_toggleSuspendFirebug').setAttribute("label", $STR("Resume Firebug"));
  270.         }
  271.         else
  272.         {
  273.             suspendMenuItem.removeAttribute("suspended");
  274.             $('menu_toggleSuspendFirebug').setAttribute("label", $STR("Suspend Firebug"));
  275.         }
  276.  
  277.         Firebug.ActivableModule.resetTooltip();
  278.     },
  279.  
  280.     toggleSuspend: function()
  281.     {
  282.         if (this.getSuspended())         // then we should not be visible,
  283.         {
  284.             if (FirebugContext.detached)
  285.             {
  286.                 FirebugContext.chrome.focus();
  287.                 this.resume();
  288.             }
  289.             else
  290.                 this.toggleBar(true);   // become visible and call resume()
  291.         }
  292.         else
  293.         {
  294.             this.suspend();
  295.             this.syncBar();  // pull down the visible UI
  296.         }
  297.     },
  298.  
  299.     suspend: function()  // dispatch suspendFirebug to all windows
  300.     {
  301.         this.broadcast('suspendFirebug', []);
  302.     },
  303.  
  304.     suspendFirebug: function() // dispatch onSuspendFirebug to all modules
  305.     {
  306.         this.setSuspended("suspending");
  307.         TabWatcher.iterateContexts(
  308.             function suspendContext(context)
  309.             {
  310.                 // turn every activable module off.
  311.                 for (var i = 0; i < activableModules.length; i++)
  312.                 {
  313.                     try
  314.                     {
  315.                         activableModules[i].onSuspendFirebug(context);
  316.                     }
  317.                     catch (e)
  318.                     {
  319.                         try
  320.                         {
  321.                             var url = (context.window && context.window.location)? context.window.location : "no context.window.location";
  322.  
  323.                         }
  324.                         catch (e2)
  325.                         {
  326.                         }
  327.                     }
  328.                     // don't show Firebug panel as another hint we are suspended.
  329.                     context.browser.showFirebug = false;
  330.                     if (context.browser.detached)
  331.                     {
  332.                         // Pulls down the UI and put up a cover showing a resume button.
  333.                         context.chrome.setChromeDocumentAttribute("fbToolbox", "collapsed", "true");
  334.                         context.chrome.setChromeDocumentAttribute("fbResumeBoxButton", "label", "Resume Firebug");
  335.                         context.chrome.setChromeDocumentAttribute("fbResumeBox", "collapsed", "false");
  336.                         context.chrome.setChromeDocumentAttribute("fbContentBox", "collapsed", "true");
  337.                     }
  338.                 }
  339.             }
  340.         );
  341.  
  342.         this.setSuspended("suspended");
  343.     },
  344.  
  345.     resume: function()
  346.     {
  347.         this.broadcast('resumeFirebug', []);
  348.     },
  349.  
  350.     resumeFirebug: function()  // dispatch onResumeFirebug to all modules
  351.     {
  352.         this.setSuspended("resuming");
  353.         TabWatcher.iterateContexts
  354.         (
  355.             function resumeContext(context)
  356.             {
  357.                 try
  358.                 {
  359.                     // turn every activable module on.
  360.                     for (var i = 0; i < activableModules.length; i++)
  361.                         activableModules[i].onResumeFirebug(context);
  362.                 }
  363.                 catch (e)
  364.                 {
  365.                 }
  366.  
  367.                 if (context.browser.detached && context.originalChrome)
  368.                 {
  369.                     // Pull down the "resume" button covering the UI, bring up the UI
  370.                     context.chrome.setChromeDocumentAttribute("fbToolbox", "collapsed", "false");
  371.                     context.chrome.setChromeDocumentAttribute("fbContentBox", "collapsed", "false");
  372.                     context.chrome.setChromeDocumentAttribute("fbResumeBox", "collapsed", "true");
  373.                 }
  374.             }
  375.         );
  376.  
  377.         this.setSuspended(null);
  378.     },
  379.  
  380.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  381.     // Dead Windows
  382.  
  383.     killWindow: function(browser, chrome)
  384.     {
  385.         deadWindows.push({browser: browser, chrome: chrome});
  386.         deadWindowTimeout = setTimeout(function() { Firebug.closeDeadWindows(); }, 3000);
  387.     },
  388.  
  389.     rescueWindow: function(browser)
  390.     {
  391.         for (var i = 0; i < deadWindows.length; ++i)
  392.         {
  393.             if (deadWindows[i].browser == browser)
  394.             {
  395.                 deadWindows.splice(i, 1);
  396.                 break;
  397.             }
  398.         }
  399.     },
  400.  
  401.     closeDeadWindows: function()
  402.     {
  403.         for (var i = 0; i < deadWindows.length; ++i)
  404.             deadWindows[i].chrome.close();
  405.  
  406.         deadWindows = [];
  407.         deadWindowTimeout = 0;
  408.     },
  409.  
  410.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  411.     // Registration
  412.  
  413.     registerModule: function()
  414.     {
  415.         modules.push.apply(modules, arguments);
  416.  
  417.         for (var i = 0; i < arguments.length; ++i)
  418.             TabWatcher.addListener(arguments[i]);
  419.                                                                                                                        /*@explore*/
  420.     },
  421.  
  422.     registerActivableModule: function()
  423.     {
  424.         activableModules.push.apply(activableModules, arguments);
  425.         this.registerModule.apply(this, arguments);
  426.     },
  427.  
  428.     registerExtension: function()
  429.     {
  430.         extensions.push.apply(extensions, arguments);
  431.  
  432.         for (var i = 0; i < arguments.length; ++i)
  433.             TabWatcher.addListener(arguments[i]);
  434.  
  435.         for (var j = 0; j < arguments.length; j++)
  436.             uiListeners.push(arguments[j]);
  437.     },
  438.  
  439.     registerPanel: function()
  440.     {
  441.         panelTypes.push.apply(panelTypes, arguments);
  442.  
  443.         for (var i = 0; i < arguments.length; ++i)
  444.             panelTypeMap[arguments[i].prototype.name] = arguments[i];
  445.                                                                                                                        /*@explore*/
  446.     },
  447.  
  448.     registerRep: function()
  449.     {
  450.         reps.push.apply(reps, arguments);
  451.     },
  452.  
  453.     setDefaultRep: function(rep)
  454.     {
  455.         defaultRep = rep;
  456.     },
  457.  
  458.     registerEditor: function()
  459.     {
  460.         editors.push.apply(editors, arguments);
  461.     },
  462.  
  463.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  464.     // Options
  465.  
  466.     togglePref: function(name)
  467.     {
  468.         this.setPref(Firebug.prefDomain, name, !this[name]);
  469.     },
  470.  
  471.     getPref: function(prefDomain, name)
  472.     {
  473.         var prefName = prefDomain + "." + name;
  474.  
  475.         var type = prefs.getPrefType(prefName);
  476.         if (type == nsIPrefBranch.PREF_STRING)
  477.             return prefs.getCharPref(prefName);
  478.         else if (type == nsIPrefBranch.PREF_INT)
  479.             return prefs.getIntPref(prefName);
  480.         else if (type == nsIPrefBranch.PREF_BOOL)
  481.             return prefs.getBoolPref(prefName);
  482.     },
  483.  
  484.     setPref: function(prefDomain, name, value)
  485.     {
  486.         var prefName = prefDomain + "." + name;
  487.  
  488.         var type = prefs.getPrefType(prefName);
  489.         if (type == nsIPrefBranch.PREF_STRING)
  490.             prefs.setCharPref(prefName, value);
  491.         else if (type == nsIPrefBranch.PREF_INT)
  492.             prefs.setIntPref(prefName, value);
  493.         else if (type == nsIPrefBranch.PREF_BOOL)
  494.             prefs.setBoolPref(prefName, value);
  495.         else if (type == nsIPrefBranch.PREF_INVALID)
  496.         {
  497.             FBTrace.sysout("firebug.setPref FAILS: Invalid preference "+prefName+" check that it is listed in defaults/prefs.js");
  498.         }
  499.  
  500.     },
  501.  
  502.     increaseTextSize: function(amt)
  503.     {
  504.         this.setTextSize(this.textSize+amt);
  505.     },
  506.  
  507.     setTextSize: function(value)
  508.     {
  509.         this.setPref(Firebug.prefDomain, "textSize", value);
  510.     },
  511.  
  512.     updatePref: function(name, value)
  513.     {
  514.         // Prevent infinite recursion due to pref observer
  515.         if ( optionUpdateMap.hasOwnProperty(name) )
  516.             return;
  517.  
  518.         optionUpdateMap[name] = 1;
  519.         this[name] = value;
  520.  
  521.         dispatch(modules, "updateOption", [name, value]);
  522.  
  523.         FirebugChrome.updateOption(name, value);
  524.  
  525.         if (TabWatcher.contexts)
  526.         {
  527.             for (var i = 0; i < TabWatcher.contexts.length; ++i)
  528.             {
  529.                 var context = TabWatcher.contexts[i];
  530.                 if (context.externalChrome)
  531.                     context.chrome.updateOption(name, value);
  532.             }
  533.         }
  534.  
  535.         if (name.substr(0, 15) == "externalEditors")
  536.         {
  537.             this.loadExternalEditors();
  538.         }
  539.  
  540.         delete optionUpdateMap[name];
  541.                                                                                                                        /*@explore*/
  542.     },
  543.  
  544.     loadExternalEditors: function()
  545.     {
  546.         const prefName = "externalEditors";
  547.         const editorPrefNames = ["label", "executable", "cmdline", "image"];
  548.  
  549.         externalEditors = [];
  550.         var list = this.getPref(this.prefDomain, prefName).split(",");
  551.         for (var i = 0; i < list.length; ++i)
  552.         {
  553.             var editorId = list[i];
  554.             if ( !editorId || editorId == "")
  555.                 continue;
  556.             var item = { id: editorId };
  557.             for( var j = 0; j < editorPrefNames.length; ++j )
  558.             {
  559.                 try {
  560.                     item[editorPrefNames[j]] = this.getPref(this.prefDomain, prefName+"."+editorId+"."+editorPrefNames[j]);
  561.                 }
  562.                 catch(exc)
  563.                 {
  564.                 }
  565.             }
  566.             if ( item.label && item.executable )
  567.             {
  568.                 if (!item.image)
  569.                     item.image = getIconURLForFile(item.executable);
  570.                 externalEditors.push(item);
  571.             }
  572.         }
  573.         return externalEditors;
  574.     },
  575.  
  576.     get registeredEditors()
  577.     {
  578.         var newArray = [];
  579.         if ( editors.length > 0 )
  580.         {
  581.             newArray.push.apply(newArray, editors);
  582.             if ( externalEditors.length > 0 )
  583.                 newArray.push("-");
  584.         }
  585.         if ( externalEditors.length > 0 )
  586.             newArray.push.apply(newArray, externalEditors);
  587.  
  588.         return newArray;
  589.     },
  590.  
  591.     openEditors: function()
  592.     {
  593.         var args = {
  594.             FBL: FBL,
  595.             prefName: this.prefDomain + ".externalEditors"
  596.         };
  597.         openWindow("Firebug:ExternalEditors", "chrome://firebug/content/editors.xul", "", args);
  598.     },
  599.  
  600.     openInEditor: function(context, editorId)
  601.     {
  602.         try {
  603.         if (!editorId)
  604.             return;
  605.  
  606.         var location;
  607.         if (context)
  608.         {
  609.             var panel = context.chrome.getSelectedPanel();
  610.             if (panel)
  611.             {
  612.                 location = panel.location;
  613.                 if (!location && panel.name == "html")
  614.                     location = context.window.document.location;
  615.                 if ( location instanceof SourceFile || location instanceof CSSStyleSheet )
  616.                     location = location.href;
  617.             }
  618.         }
  619.         if (!location)
  620.         {
  621.             if (tabBrowser.currentURI)
  622.                 location = tabBrowser.currentURI.asciiSpec;
  623.         }
  624.         if (!location)
  625.             return;
  626.         location = location.toString();
  627.         if (Firebug.filterSystemURLs && isSystemURL(location))
  628.             return;
  629.  
  630.         var list = extendArray(editors, externalEditors);
  631.         var editor = null;
  632.         for( var i = 0; i < list.length; ++i )
  633.         {
  634.             if (editorId == list[i].id)
  635.             {
  636.                 editor = list[i];
  637.                 break;
  638.             }
  639.         }
  640.         if (editor)
  641.         {
  642.             if (editor.handler)
  643.             {
  644.                 editor.handler(location);
  645.                 return;
  646.             }
  647.             var args = [];
  648.             var localFile = null;
  649.             var targetAdded = false;
  650.             if (editor.cmdline)
  651.             {
  652.                 args = editor.cmdline.split(" ");
  653.                 for( var i = 0; i < args.length; ++i )
  654.                 {
  655.                     if ( args[i] == "%url" )
  656.                     {
  657.                         args[i] = location;
  658.                         targetAdded = true;
  659.                     }
  660.                     else if ( args[i] == "%file" )
  661.                     {
  662.                         if (!localFile)
  663.                             localFile = this.getLocalSourceFile(context, location);
  664.                         args[i] = localFile;
  665.                         targetAdded = true;
  666.                     }
  667.                 }
  668.             }
  669.             if (!targetAdded)
  670.             {
  671.                 localFile = this.getLocalSourceFile(context, location);
  672.                 if (!localFile)
  673.                     return;
  674.                 args.push(localFile);
  675.             }
  676.             FBL.launchProgram(editor.executable, args);
  677.         }
  678.         }catch(exc) { ERROR(exc); }
  679.     },
  680.  
  681.     getLocalSourceFile: function(context, href)
  682.     {
  683.         if ( isLocalURL(href) )
  684.             return getLocalPath(href);
  685.         var data;
  686.         if (context)
  687.         {
  688.             data = context.sourceCache.loadText(href);
  689.         } else
  690.         {
  691.             var ctx = { browser: tabBrowser.selectedBrowser, window: tabBrowser.selectedBrowser.contentWindow };
  692.             data = new SourceCache(ctx).loadText(href);
  693.         }
  694.         if (!data)
  695.             return;
  696.         if (!temporaryDirectory)
  697.         {
  698.             var tmpDir = DirService.getFile(NS_OS_TEMP_DIR, {});
  699.             tmpDir.append("fbtmp");
  700.             tmpDir.createUnique(nsIFile.DIRECTORY_TYPE, 0775);
  701.             temporaryDirectory = tmpDir;
  702.         }
  703.  
  704.         var lpath = href.replace(/^[^:]+:\/*/g, "").replace(/\?.*$/g, "").replace(/[^0-9a-zA-Z\/.]/g, "_");
  705.         /* dummy comment to workaround eclipse bug */
  706.         if ( !/\.[\w]{1,5}$/.test(lpath) )
  707.         {
  708.             if ( lpath.charAt(lpath.length-1) == '/' )
  709.                 lpath += "index";
  710.             lpath += ".html";
  711.         }
  712.         if ( getPlatformName() == "WINNT" )
  713.             lpath = lpath.replace(/\//g, "\\");
  714.         var file = QI(temporaryDirectory.clone(), nsILocalFile);
  715.         file.appendRelativePath(lpath);
  716.         if (!file.exists())
  717.             file.create(nsIFile.NORMAL_FILE_TYPE, 0664);
  718.         temporaryFiles.push(file.path);
  719.  
  720.         var stream = CCIN("@mozilla.org/network/safe-file-output-stream;1", "nsIFileOutputStream");
  721.         stream.init(file, 0x04 | 0x08 | 0x20, 0664, 0); // write, create, truncate
  722.         stream.write(data, data.length);
  723.         if (stream instanceof nsISafeOutputStream)
  724.             stream.finish();
  725.         else
  726.             stream.close();
  727.  
  728.         return file.path;
  729.     },
  730.  
  731.     deleteTemporaryFiles: function()
  732.     {
  733.         try {
  734.             var file = CCIN("@mozilla.org/file/local;1", "nsILocalFile");
  735.             for( var i = 0; i < temporaryFiles.length; ++i)
  736.             {
  737.                 file.initWithPath(temporaryFiles[i]);
  738.                 if (file.exists())
  739.                     file.remove(false);
  740.             }
  741.         }
  742.         catch(exc)
  743.         {
  744.         }
  745.         try {
  746.             if (temporaryDirectory && temporaryDirectory.exists())
  747.                 temporaryDirectory.remove(true);
  748.         } catch(exc)
  749.         {
  750.         }
  751.     },
  752.  
  753.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  754.     // Browser Bottom Bar
  755.  
  756.     showBar: function(show)
  757.     {
  758.         var browser = FirebugChrome.getCurrentBrowser();
  759.         browser.showFirebug = show;
  760.  
  761.         var shouldShow = show && !browser.detached;
  762.         contentBox.setAttribute("collapsed", !shouldShow);
  763.         contentSplitter.setAttribute("collapsed", !shouldShow);
  764.         toggleCommand.setAttribute("checked", !!shouldShow);
  765.         detachCommand.setAttribute("checked", !!browser.detached);
  766.         this.showKeys(shouldShow);
  767.  
  768.         dispatch(modules, show ? "showUI" : "hideUI", [browser, FirebugContext]);
  769.     },
  770.  
  771.     showKeys: function(shouldShow)
  772.     {
  773.         var keyset = document.getElementById("mainKeyset");
  774.         var keys = FBL.getElementByClass(keyset, "fbOnlyKey");
  775.         for (var i = 0; i < keys.length; i++)
  776.         {
  777.             keys[i].setAttribute("disabled", !!shouldShow);
  778.         }
  779.     },
  780.  
  781.     toggleBar: function(forceOpen, panelName)
  782.     {
  783.         if (Firebug.openInWindow)
  784.             return this.toggleDetachBar(true);
  785.  
  786.         var browser = FirebugChrome.getCurrentBrowser();
  787.         if (!browser.chrome) // then a page without a context
  788.         {
  789.             // Check to see if the context was skipped while suspended.
  790.             if (this.getSuspended() && TabWatcher.watchBrowser(browser))
  791.                 this.resume();
  792.             else
  793.                 return;
  794.         }
  795.  
  796.         var toggleOff = (forceOpen == undefined) ? !contentBox.collapsed : !forceOpen;
  797.         if (toggleOff == contentBox.collapsed)
  798.             return;
  799.  
  800.         if (this.getSuspended())
  801.             this.resume();
  802.  
  803.         if (panelName)
  804.             browser.chrome.selectPanel(panelName);
  805.  
  806.         if (browser.detached)
  807.             browser.chrome.focus();
  808.         else
  809.         {
  810.             if (toggleOff)
  811.                 browser.chrome.hidePanel();
  812.             else
  813.                 browser.chrome.syncPanel();
  814.  
  815.             this.showBar(!toggleOff);
  816.         }
  817.     },
  818.  
  819.     toggleDetachBar: function(forceOpen)
  820.     {
  821.         var browser = FirebugChrome.getCurrentBrowser();
  822.         if (!forceOpen && browser.detached)
  823.         {
  824.             browser.chrome.close();
  825.             detachCommand.setAttribute("checked", false);
  826.         }
  827.         else
  828.             this.detachBar();
  829.     },
  830.  
  831.     detachBar: function()
  832.     {
  833.         var browser = FirebugChrome.getCurrentBrowser();
  834.         if (!browser.chrome)
  835.             return;
  836.  
  837.         if (browser.detached)
  838.             browser.chrome.focus();
  839.         else
  840.         {
  841.             if (FirebugContext)
  842.                 FirebugContext.detached = true;
  843.  
  844.             browser.detached = true;
  845.  
  846.             var args = {
  847.                     FBL: FBL,
  848.                     Firebug: this,
  849.                     browser: browser,
  850.                     context: FirebugContext
  851.             };
  852.             openWindow("Firebug", "chrome://firebug/content/firebug.xul", "", args);
  853.             detachCommand.setAttribute("checked", true);
  854.  
  855.             FirebugChrome.clearPanels();
  856.             this.syncBar();
  857.         }
  858.     },
  859.  
  860.     syncBar: function()
  861.     {
  862.         var browser = FirebugChrome.getCurrentBrowser();
  863.         this.showBar(browser && browser.showFirebug);
  864.     },
  865.  
  866.     onClickStatusIcon: function(context, event)
  867.     {
  868.         if (event.button != 0)
  869.             return;
  870.         else if (isControl(event))
  871.             this.toggleDetachBar(true);
  872.         else if (context && context.errorCount)
  873.             Firebug.toggleBar(undefined, 'console');
  874.         else
  875.             this.toggleBar();
  876.     },
  877.  
  878.     onClickStatusText: function(context, event)
  879.     {
  880.         if (event.button != 0)
  881.             return;
  882.  
  883.         if (!context || !context.errorCount)
  884.             return;
  885.  
  886.         var browser = FirebugChrome.getCurrentBrowser();
  887.         if (!browser.chrome)
  888.             return;
  889.  
  890.         var panel = browser.chrome.getSelectedPanel();
  891.         if (panel && panel.name != "console")
  892.         {
  893.             browser.chrome.selectPanel("console");
  894.             cancelEvent(event);
  895.         }
  896.     },
  897.  
  898.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  899.     // Panels
  900.  
  901.     getPanelType: function(panelName)
  902.     {
  903.         if (panelTypeMap.hasOwnProperty(panelName))
  904.             return panelTypeMap[panelName];
  905.         else
  906.             return null;
  907.     },
  908.  
  909.     getPanelTitle: function(panelType)
  910.     {
  911.         return panelType.prototype.title ? panelType.prototype.title
  912.             : FBL.$STR("Panel-"+panelType.prototype.name);
  913.     },
  914.  
  915.     getMainPanelTypes: function(context)
  916.     {
  917.         var resultTypes = [];
  918.  
  919.         for (var i = 0; i < panelTypes.length; ++i)
  920.         {
  921.             var panelType = panelTypes[i];
  922.             if (!panelType.prototype.parentPanel)
  923.                 resultTypes.push(panelType);
  924.         }
  925.  
  926.         if (context.panelTypes)
  927.         {
  928.             for (var i = 0; i < context.panelTypes.length; ++i)
  929.             {
  930.                 var panelType = context.panelTypes[i];
  931.                 if (!panelType.prototype.parentPanel)
  932.                     resultTypes.push(panelType);
  933.             }
  934.         }
  935.  
  936.         return resultTypes;
  937.     },
  938.  
  939.     getSidePanelTypes: function(context, mainPanel)
  940.     {
  941.         if (!mainPanel)
  942.             return [];
  943.  
  944.         var resultTypes = [];
  945.  
  946.         for (var i = 0; i < panelTypes.length; ++i)
  947.         {
  948.             var panelType = panelTypes[i];
  949.             if (panelType.prototype.parentPanel && (panelType.prototype.parentPanel == mainPanel.name) )
  950.                 resultTypes.push(panelType);
  951.         }
  952.  
  953.         if (context.panelTypes)
  954.         {
  955.             for (var i = 0; i < context.panelTypes.length; ++i)
  956.             {
  957.                 var panelType = context.panelTypes[i];
  958.                 if (panelType.prototype.parentPanel == mainPanel.name)
  959.                     resultTypes.push(panelType);
  960.             }
  961.         }
  962.  
  963.         resultTypes.sort(function(a, b)
  964.         {
  965.             return a.prototype.order < b.prototype.order ? -1 : 1;
  966.         });
  967.  
  968.         return resultTypes;
  969.     },
  970.  
  971.     /**
  972.      * Gets an object containing the state of the panel from the last time
  973.      * it was displayed before one or more page reloads.
  974.      */
  975.     getPanelState: function(panel)
  976.     {
  977.         var persistedState = panel.context.persistedState;
  978.         return persistedState ? persistedState.panelState[panel.name] : null;
  979.     },
  980.  
  981.     showPanel: function(browser, panel)
  982.     {
  983.         dispatch(modules, "showPanel", [browser, panel]);
  984.     },
  985.  
  986.     showSidePanel: function(browser, panel)
  987.     {
  988.         dispatch(modules, "showSidePanel", [browser, panel]);
  989.     },
  990.  
  991.     reattachContext: function(browser, context)
  992.     {
  993.         dispatch(modules, "reattachContext", [browser, context]);
  994.     },
  995.  
  996.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  997.     // URL mapping
  998.  
  999.     getObjectByURL: function(context, url)
  1000.     {
  1001.         for (var i = 0; i < modules.length; ++i)
  1002.         {
  1003.             var object = modules[i].getObjectByURL(context, url);
  1004.             if (object)
  1005.                 return object;
  1006.         }
  1007.     },
  1008.  
  1009.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1010.     // Reps
  1011.  
  1012.     getRep: function(object)
  1013.     {
  1014.         var type = typeof(object);
  1015.         for (var i = 0; i < reps.length; ++i)
  1016.         {
  1017.             var rep = reps[i];
  1018.             try
  1019.             {
  1020.                 if (rep.supportsObject(object, type))
  1021.                     return rep;
  1022.             }
  1023.             catch (exc)
  1024.             {
  1025.             }
  1026.         }
  1027.  
  1028.         return defaultRep;
  1029.     },
  1030.  
  1031.     getRepObject: function(node)
  1032.     {
  1033.         var target = null;
  1034.         for (var child = node; child; child = child.parentNode)
  1035.         {
  1036.             if (hasClass(child, "repTarget"))
  1037.                 target = child;
  1038.  
  1039.             if (child.repObject)
  1040.             {
  1041.                 if (!target && hasClass(child, "repIgnore"))
  1042.                     break;
  1043.                 else
  1044.                     return child.repObject;
  1045.             }
  1046.         }
  1047.     },
  1048.  
  1049.     getRepNode: function(node)
  1050.     {
  1051.         for (var child = node; child; child = child.parentNode)
  1052.         {
  1053.             if (child.repObject)
  1054.                 return child;
  1055.         }
  1056.     },
  1057.  
  1058.     getElementByRepObject: function(element, object)
  1059.     {
  1060.         for (var child = element.firstChild; child; child = child.nextSibling)
  1061.         {
  1062.             if (child.repObject == object)
  1063.                 return child;
  1064.         }
  1065.     },
  1066.  
  1067.     /**
  1068.      * Takes an element from a panel document and finds the owning panel.
  1069.      */
  1070.     getElementPanel: function(element)
  1071.     {
  1072.         for (; element; element = element.parentNode)
  1073.         {
  1074.             if (element.ownerPanel)
  1075.                 return element.ownerPanel;
  1076.         }
  1077.     },
  1078.  
  1079.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1080.  
  1081.     visitWebsite: function(which)
  1082.     {
  1083.         openNewTab(firebugURLs[which]);
  1084.     },
  1085.  
  1086.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1087.     // nsISupports
  1088.  
  1089.     QueryInterface : function(iid)
  1090.     {
  1091.         if (iid.equals(nsIFireBugClient) || iid.equals(nsISupports))
  1092.         {
  1093.             return this;
  1094.         }
  1095.  
  1096.         throw Components.results.NS_NOINTERFACE;
  1097.     },
  1098.  
  1099.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1100.     // nsIPrefObserver
  1101.     observe: function(subject, topic, data)
  1102.     {
  1103.         if (data.indexOf("extensions.") == -1)
  1104.             return;
  1105.  
  1106.         if (data.substring(0, Firebug.prefDomain.length) == Firebug.prefDomain)
  1107.             var domain = Firebug.prefDomain;
  1108.         if (data.substring(0, Firebug.servicePrefDomain.length) == Firebug.servicePrefDomain)
  1109.             var domain = Firebug.servicePrefDomain;
  1110.  
  1111.         if (domain)
  1112.         {
  1113.             var name = data.substr(domain.length+1);
  1114.             var value = this.getPref(domain, name);
  1115.             this.updatePref(name, value);
  1116.         }
  1117.     },
  1118.  
  1119.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1120.     // nsIFireBugClient  These are per XUL window callbacks
  1121.  
  1122.     enable: function()  // Called by firebug-service when the first context is created.
  1123.     {
  1124.         dispatch(modules, "enable");
  1125.     },
  1126.  
  1127.     disable: function()
  1128.     {
  1129.         dispatch(modules, "disable");
  1130.     },
  1131.  
  1132.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1133.     // TabWatcher Owner
  1134.  
  1135.     enableContext: function(win, uri)  // currently this can be called with nsIURI or a string URL.
  1136.     {
  1137.         if ( dispatch2(extensions, "acceptContext", [win, uri]) )
  1138.             return true;
  1139.         if ( dispatch2(extensions, "declineContext", [win, uri]) )
  1140.             return false;
  1141.  
  1142.         if (Firebug.getSuspended())  // during suspend we will not create new contexts
  1143.             return false;
  1144.         return true;
  1145.     },
  1146.  
  1147.     createTabContext: function(win, browser, chrome, state)
  1148.     {
  1149.         return new Firebug.TabContext(win, browser, chrome, state);
  1150.     },
  1151.  
  1152.     destroyTabContext: function(browser, context)
  1153.     {
  1154.         if (context)
  1155.         {
  1156.             // Persist remnants of the context for restoration if the user reloads
  1157.             context.browser.panelName = context.panelName;
  1158.             context.browser.sidePanelNames = context.sidePanelNames;
  1159.  
  1160.             if (browser.detached || context == FirebugContext)
  1161.             {
  1162.                 clearContextTimeout = setTimeout(function delayClearContext()
  1163.                 {
  1164.                     if (context == FirebugContext)
  1165.                     {
  1166.                         browser.isSystemPage = true;  // XXXjjb I don't believe this is ever tested.
  1167.                         Firebug.showContext(browser, null);
  1168.                     }
  1169.                 }, 100);
  1170.  
  1171.                 browser.chrome.clearPanels();
  1172.             }
  1173.  
  1174.             if (context.externalChrome)
  1175.             {
  1176.                 if (browser.firebugReload)
  1177.                     delete browser.firebugReload; // and don't kiiWindow
  1178.                 else
  1179.                     this.killWindow(context.browser, context.externalChrome);
  1180.             }
  1181.         }
  1182.         else if (browser.detached)
  1183.             this.killWindow(browser, browser.chrome);
  1184.     },
  1185.  
  1186.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1187.     // TabWatcher Listener
  1188.  
  1189.     initContext: function(context)
  1190.     {
  1191.         context.panelName = context.browser.panelName;
  1192.         if (context.browser.sidePanelNames)
  1193.             context.sidePanelNames = context.browser.sidePanelNames;
  1194.     },
  1195.  
  1196.     showContext: function(browser, context)
  1197.     {
  1198.         if (clearContextTimeout)
  1199.         {
  1200.             clearTimeout(clearContextTimeout);
  1201.             clearContextTimeout = 0;
  1202.         }
  1203.  
  1204.         if (deadWindowTimeout)
  1205.             this.rescueWindow(browser);
  1206.  
  1207.         if (context)
  1208.         {
  1209.             if (browser)
  1210.                 browser.chrome.showContext(browser, context);  // if context null, no-op
  1211.  
  1212.             FirebugContext = context;
  1213.  
  1214.             /* XXXjjb this implements "auto-suspend", but it has at least one side effect see issue 1073.
  1215.                reconsider for 1.3
  1216.             if (this.isDisabledFor(FirebugContext))
  1217.             {
  1218.                     var browser = FirebugChrome.getCurrentBrowser();
  1219.                     if (browser && !browser.detached && !browser.showFirebug)
  1220.                         this.suspend();
  1221.             }
  1222.             */
  1223.  
  1224.             this.syncBar();
  1225.         }
  1226.     },
  1227.  
  1228.     isDisabledFor: function(context)
  1229.     {
  1230.         for (var i = 0; i < activableModules.length; i++)
  1231.             if (activableModules[i].isEnabled(context)) return false;
  1232.         return true;
  1233.     },
  1234.  
  1235.     watchWindow: function(context, win)
  1236.     {
  1237.         for (var panelName in context.panelMap)
  1238.         {
  1239.             var panel = context.panelMap[panelName];
  1240.             panel.watchWindow(win);
  1241.         }
  1242.     },
  1243.  
  1244.     unwatchWindow: function(context, win)
  1245.     {
  1246.         for (var panelName in context.panelMap)
  1247.         {
  1248.             var panel = context.panelMap[panelName];
  1249.             panel.unwatchWindow(win);
  1250.         }
  1251.     },
  1252.  
  1253.     loadedContext: function(context)
  1254.     {
  1255.         // re-synchronize after load if this context is showing
  1256.         if (this.tabBrowser.currentURI.spec == context.browser.currentURI.spec)
  1257.             context.browser.chrome.showContext(context.browser, context);
  1258.     },
  1259.     //***********************************************************************
  1260.  
  1261.     getTabIdForWindow: function(aWindow)
  1262.     {
  1263.         aWindow = getRootWindow(aWindow);
  1264.  
  1265.         if (!aWindow || !this.tabBrowser.getBrowserIndexForDocument)
  1266.             return null;
  1267.  
  1268.         try {
  1269.             var targetDoc = aWindow.document;
  1270.  
  1271.             var tab = null;
  1272.             var targetBrowserIndex = this.tabBrowser.getBrowserIndexForDocument(targetDoc);
  1273.  
  1274.             if (targetBrowserIndex != -1)
  1275.             {
  1276.                 tab = this.tabBrowser.tabContainer.childNodes[targetBrowserIndex];
  1277.                 return tab.linkedPanel;
  1278.             }
  1279.         } catch (ex) {}
  1280.  
  1281.         return null;
  1282.     },
  1283.  
  1284. };
  1285.  
  1286. // ************************************************************************************************
  1287.  
  1288. Firebug.Module =
  1289. {
  1290.     /**
  1291.      * Called when the window is opened.
  1292.      */
  1293.     initialize: function()
  1294.     {
  1295.     },
  1296.  
  1297.     /**
  1298.      * Called when the UI is ready for context creation.
  1299.      * Used by chromebug; normally FrameProgressListener events trigger UI synchronization,
  1300.      * this event allows sync without progress events.
  1301.      */
  1302.     initializeUI: function(detachArgs)
  1303.     {
  1304.     },
  1305.  
  1306.     /**
  1307.      * Called when the window is closed.
  1308.      */
  1309.     shutdown: function()
  1310.     {
  1311.  
  1312.     },
  1313.  
  1314.     /**
  1315.      * Called when a new context is created but before the page is loaded.
  1316.      */
  1317.     initContext: function(context)
  1318.     {
  1319.     },
  1320.  
  1321.     /**
  1322.      * Called after a context is detached to a separate window;
  1323.      */
  1324.     reattachContext: function(browser, context)
  1325.     {
  1326.     },
  1327.  
  1328.     /**
  1329.      * Called when a context is destroyed. Module may store info on persistedState for reloaded pages.
  1330.      */
  1331.     destroyContext: function(context, persistedState)
  1332.     {
  1333.     },
  1334.  
  1335.     /**
  1336.      * Called when attaching to a window (top-level or frame).
  1337.      */
  1338.     watchWindow: function(context, win)
  1339.     {
  1340.     },
  1341.  
  1342.     /**
  1343.      * Called when unwatching a window (top-level or frame).
  1344.      */
  1345.     unwatchWindow: function(context, win)
  1346.     {
  1347.     },
  1348.  
  1349.     // Called when a FF tab is create or activated (user changes FF tab)
  1350.     // Called after context is created or with context == null (to abort?)
  1351.     showContext: function(browser, context)
  1352.     {
  1353.     },
  1354.  
  1355.     /**
  1356.      * Called after a context's page gets DOMContentLoaded
  1357.      */
  1358.     loadedContext: function(context)
  1359.     {
  1360.     },
  1361.  
  1362.     showPanel: function(browser, panel)
  1363.     {
  1364.     },
  1365.  
  1366.     showSidePanel: function(browser, panel)
  1367.     {
  1368.     },
  1369.  
  1370.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1371.  
  1372.     updateOption: function(name, value)
  1373.     {
  1374.     },
  1375.  
  1376.     getObjectByURL: function(context, url)
  1377.     {
  1378.     },
  1379.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1380.     // intermodule dependency
  1381.     
  1382.     // caller needs module. win maybe context.window or iframe in context.window.
  1383.     // true means module is ready now, else getting ready
  1384.     isNeededGetReady: function(context, win) 
  1385.     {
  1386.     },
  1387. };
  1388.  
  1389. // ************************************************************************************************
  1390.  
  1391. Firebug.Extension =
  1392. {
  1393.     acceptContext: function(win,uri)
  1394.     {
  1395.         return false;
  1396.     },
  1397.  
  1398.     declineContext: function(win,uri)
  1399.     {
  1400.         return false;
  1401.     }
  1402. };
  1403.  
  1404. // ************************************************************************************************
  1405.  
  1406. Firebug.Panel =
  1407. {
  1408.     searchable: false,
  1409.     editable: true,
  1410.     order: 2147483647,
  1411.     statusSeparator: "<",
  1412.  
  1413.     initialize: function(context, doc)
  1414.     {
  1415.         this.context = context;
  1416.         this.document = doc;
  1417.  
  1418.         this.panelNode = doc.createElement("div");
  1419.         this.panelNode.ownerPanel = this;
  1420.  
  1421.         setClass(this.panelNode, "panelNode panelNode-"+this.name+" contextUID="+context.uid);
  1422.         doc.body.appendChild(this.panelNode);
  1423.  
  1424.         this.initializeNode(this.panelNode);
  1425.     },
  1426.  
  1427.     destroy: function(state) // Panel may store info on state
  1428.     {
  1429.         if (this.panelNode)
  1430.             delete this.panelNode.ownerPanel;
  1431.  
  1432.         this.destroyNode();
  1433.     },
  1434.  
  1435.     detach: function(oldChrome, newChrome)
  1436.     {
  1437.         this.lastScrollTop = this.panelNode.scrollTop;
  1438.     },
  1439.  
  1440.     reattach: function(doc)
  1441.     {
  1442.         this.document = doc;
  1443.  
  1444.         if (this.panelNode)
  1445.         {
  1446.             this.panelNode = doc.adoptNode(this.panelNode, true);
  1447.             this.panelNode.ownerPanel = this;
  1448.             doc.body.appendChild(this.panelNode);
  1449.             this.panelNode.scrollTop = this.lastScrollTop;
  1450.             delete this.lastScrollTop;
  1451.         }
  1452.     },
  1453.  
  1454.     // Called after module.initialize; addEventListener-s here
  1455.     initializeNode: function(myPanelNode)
  1456.     {
  1457.     },
  1458.  
  1459.     // removeEventListener-s here.
  1460.     destroyNode: function()
  1461.     {
  1462.     },
  1463.  
  1464.     show: function(state)  // persistedPanelState plus non-persisted hide() values
  1465.     {
  1466.     },
  1467.  
  1468.     hide: function(state)  // store info on state for next show.
  1469.     {
  1470.     },
  1471.  
  1472.     watchWindow: function(win)
  1473.     {
  1474.     },
  1475.  
  1476.     unwatchWindow: function(win)
  1477.     {
  1478.     },
  1479.  
  1480.     updateOption: function(name, value)
  1481.     {
  1482.     },
  1483.  
  1484.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1485.  
  1486.     /**
  1487.      * Toolbar helpers
  1488.      */
  1489.     showToolbarButtons: function(buttonsId, show)
  1490.     {
  1491.         try
  1492.         {
  1493.             if (!this.context.browser) // XXXjjb this is bug. Somehow the panel context is not FirebugContext.
  1494.             {
  1495.             }
  1496.             var buttons = this.context.browser.chrome.$(buttonsId);
  1497.             if (buttons)
  1498.                 collapse(buttons, show ? "false" : "true");
  1499.         }
  1500.         catch (exc)
  1501.         {
  1502.         }
  1503.     },
  1504.  
  1505.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1506.  
  1507.     /**
  1508.      * Returns a number indicating the view's ability to inspect the object.
  1509.      *
  1510.      * Zero means not supported, and higher numbers indicate specificity.
  1511.      */
  1512.     supportsObject: function(object)
  1513.     {
  1514.         return 0;
  1515.     },
  1516.  
  1517.     navigate: function(object)
  1518.     {
  1519.         if (!object)
  1520.             object = this.getDefaultLocation(this.context);
  1521.  
  1522.         if (object != this.location)
  1523.         {
  1524.             this.location = object;
  1525.             this.updateLocation(object);
  1526.  
  1527.             // XXXjoe This is kind of cheating, but, feh.
  1528.             this.context.chrome.onPanelNavigate(object, this);
  1529.             if (uiListeners.length > 0) dispatch(uiListeners, "onPanelNavigate", [object, this]);  // TODO: make this.context.chrome a uiListener
  1530.         }
  1531.     },
  1532.  
  1533.     updateLocation: function(object)
  1534.     {
  1535.     },
  1536.  
  1537.     select: function(object, forceUpdate)
  1538.     {
  1539.         if (!object)
  1540.             object = this.getDefaultSelection(this.context);
  1541.  
  1542.         if (forceUpdate || object != this.selection)
  1543.         {
  1544.             this.selection = object;
  1545.             this.updateSelection(object);
  1546.  
  1547.             // XXXjoe This is kind of cheating, but, feh.
  1548.             this.context.chrome.onPanelSelect(object, this);
  1549.             if (uiListeners.length > 0)
  1550.                 dispatch(uiListeners, "onPanelSelect", [object, this]);  // TODO: make this.context.chrome a uiListener
  1551.         }
  1552.     },
  1553.  
  1554.  
  1555.     updateSelection: function(object)
  1556.     {
  1557.     },
  1558.  
  1559.     refresh: function()
  1560.     {
  1561.  
  1562.     },
  1563.  
  1564.     markChange: function(skipSelf)
  1565.     {
  1566.         if (this.dependents)
  1567.         {
  1568.             if (skipSelf)
  1569.             {
  1570.                 for (var i = 0; i < this.dependents.length; ++i)
  1571.                 {
  1572.                     var panelName = this.dependents[i];
  1573.                     if (panelName != this.name)
  1574.                         this.context.invalidatePanels(panelName);
  1575.                 }
  1576.             }
  1577.             else
  1578.                 this.context.invalidatePanels.apply(this.context, this.dependents);
  1579.         }
  1580.     },
  1581.  
  1582.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1583.  
  1584.     startInspecting: function()
  1585.     {
  1586.     },
  1587.  
  1588.     stopInspecting: function(object, cancelled)
  1589.     {
  1590.     },
  1591.  
  1592.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1593.  
  1594.     search: function(text)
  1595.     {
  1596.     },
  1597.  
  1598.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1599.  
  1600.     // An array of objects that answer to getObjectLocation.
  1601.     // Only shown if panel.location defined and supportsObject true
  1602.     getLocationList: function()
  1603.     {
  1604.         return null;
  1605.     },
  1606.  
  1607.     // Called when "Options" clicked. Return array of
  1608.     // {label: 'name', nol10n: true,  type: "checkbox", checked: <value>, command:function to set <value>}
  1609.     getOptionsMenuItems: function()
  1610.     {
  1611.         return null;
  1612.     },
  1613.  
  1614.     getContextMenuItems: function(object, target)
  1615.     {
  1616.         return [];
  1617.     },
  1618.  
  1619.     getEditor: function(target, value)
  1620.     {
  1621.     },
  1622.  
  1623.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1624.  
  1625.     getDefaultLocation: function(context)
  1626.     {
  1627.         return null;
  1628.     },
  1629.  
  1630.     getDefaultSelection: function(context)
  1631.     {
  1632.         return null;
  1633.     },
  1634.  
  1635.     browseObject: function(object)
  1636.     {
  1637.     },
  1638.  
  1639.     getPopupObject: function(target)
  1640.     {
  1641.         return Firebug.getRepObject(target);
  1642.     },
  1643.  
  1644.     getTooltipObject: function(target)
  1645.     {
  1646.         return Firebug.getRepObject(target);
  1647.     },
  1648.  
  1649.     showInfoTip: function(infoTip, x, y)
  1650.     {
  1651.  
  1652.     },
  1653.  
  1654.     getObjectPath: function(object)
  1655.     {
  1656.         return null;
  1657.     },
  1658.  
  1659.     getObjectLocation: function(object)
  1660.     {
  1661.         return "";
  1662.     },
  1663.  
  1664.     // return.path: group/category label, return.name: item label
  1665.     getObjectDescription: function(object)
  1666.     {
  1667.         var url = this.getObjectLocation(object);
  1668.         return FBL.splitURLBase(url);
  1669.     }
  1670.  
  1671. };
  1672.  
  1673. //************************************************************************************************
  1674.  
  1675. Firebug.AblePanel = extend(Firebug.Panel, 
  1676. {
  1677.     enablePanel: function()
  1678.     {
  1679.         var persistedPanelState = getPersistedState(this.context, this.name);
  1680.         persistedPanelState.enabled = true;
  1681.  
  1682.         var tab = this.getTab();
  1683.         if (tab)
  1684.             tab.removeAttribute("disabled");
  1685.     },
  1686.  
  1687.     disablePanel: function()
  1688.     {
  1689.         var persistedPanelState = getPersistedState(this.context, this.name);
  1690.         persistedPanelState.enabled = false;
  1691.  
  1692.         var tab = this.getTab();
  1693.         if (tab)
  1694.             tab.setAttribute("disabled", "true");
  1695.  
  1696.         clearNode(this.panelNode);
  1697.     },
  1698.     
  1699.     getTab: function()
  1700.     {
  1701.         var chrome = this.context ? this.context.chrome : FirebugChrome;
  1702.  
  1703.         var tab = chrome.$("fbPanelBar2").getTab(this.name);
  1704.         if (!tab)
  1705.             tab = chrome.$("fbPanelBar1").getTab(this.name);
  1706.         return tab;
  1707.     }
  1708. });
  1709.  
  1710. // ************************************************************************************************
  1711.  
  1712. Firebug.SourceBoxPanel = function() {} // XXjjb attach Firebug so this panel can be extended.
  1713.  
  1714. Firebug.SourceBoxPanel = extend(Firebug.AblePanel,
  1715. {
  1716.     initialize: function(context, doc)
  1717.     {
  1718.         Firebug.Panel.initialize.apply(this, arguments);
  1719.         this.onResize =  bind(this.onResize, this);
  1720.         contentBox.addEventListener("resize", this.onResize, true);
  1721.     },
  1722.     
  1723.     destroy: function(state)
  1724.     {
  1725.         Firebug.Panel.destroy.apply(this, arguments);
  1726.         contentBox.removeEventListener("resize", this.onResize, true);
  1727.     },
  1728.     
  1729.     // ******* override in extenders ********
  1730.     updateSourceBox: function(sourceBox)
  1731.     {
  1732.         // called just before box is shown
  1733.     },
  1734.  
  1735.     getDecorator: function(sourceBox)
  1736.     {
  1737.         // called at sourceBox creation, return a function to be called on a delay after the view port is updated.
  1738.         return function decorate(sourceBox, sourceFile)
  1739.         {
  1740.         };
  1741.     },
  1742.  
  1743.     getSourceType: function()
  1744.     {
  1745.         // eg "js" or "css"
  1746.         throw "Need to override in extender";
  1747.     },
  1748.     
  1749.     // **************************************
  1750.  
  1751.     initializeSourceBoxes: function()
  1752.     {
  1753.         this.sourceBoxes = {};
  1754.         this.anonSourceBoxes = []; // XXXjjb I don't think these are used now, everything is in the sourceCache
  1755.     },
  1756.  
  1757.     showSourceBox: function(sourceBox)
  1758.     {
  1759.         if (this.selectedSourceBox)
  1760.             collapse(this.selectedSourceBox, true);
  1761.  
  1762.         this.selectedSourceBox = sourceBox;
  1763.         delete this.currentSearch;
  1764.  
  1765.         if (sourceBox)
  1766.         {
  1767.             this.updateSourceBox(sourceBox);
  1768.             collapse(sourceBox, false);
  1769.         }
  1770.     },
  1771.  
  1772.     createSourceBox: function(sourceFile, sourceBoxDecorator)  // decorator(sourceFile, sourceBox)
  1773.     {
  1774.         var lines = loadScriptLines(sourceFile, this.context);
  1775.         if (!lines)
  1776.         {
  1777.             lines = ["Failed to load source for sourceFile "+sourceFile];
  1778.         }
  1779.  
  1780.         var sourceBox = this.document.createElement("div");
  1781.         sourceBox.repObject = sourceFile;
  1782.         setClass(sourceBox, "sourceBox");
  1783.         collapse(sourceBox, true);
  1784.  
  1785.         sourceBox.maxLineNoChars = (lines.length + "").length;
  1786.         sourceBox.lines = lines;
  1787.         sourceBox.getLineAsHTML = getSourceBoxLineAsHTML;
  1788.  
  1789.         sourceBox.min = 0;
  1790.         if (sourceFile.lineNumberShift)
  1791.             sourceBox.min = sourceBox.min + sourceFile.lineNumberShift;
  1792.  
  1793.         sourceBox.totalMax = lines.length;
  1794.         if (sourceFile.lineNumberShift)
  1795.             sourceBox.totalMax = sourceBox.totalMax + sourceFile.lineNumberShift; // eg -1
  1796.  
  1797.         sourceBox.decorator = sourceBoxDecorator;
  1798.         sourceBox.getLineNode = getLineNodeIfViewable;
  1799.  
  1800.         var lineNoCharsSpacer = "";
  1801.         for (var i = 0; i < sourceBox.maxLineNoChars; i++)
  1802.             lineNoCharsSpacer += "0";
  1803.             
  1804.         var paddedSource = 
  1805.             "<div class='topSourcePadding'>" +
  1806.                 "<div class='sourceRow'><div class='sourceLine'></div><div class='sourceRowText'></div></div>"+
  1807.             "</div>"+
  1808.             "<div class='sourceViewport'></div>"+
  1809.             "<div class='bottomSourcePadding'>"+
  1810.                 "<div class='sourceRow'><div class='sourceLine'></div><div class='sourceRowText'></div></div>"+
  1811.             "<div>";
  1812.         appendInnerHTML(sourceBox, paddedSource);
  1813.  
  1814.         // Initial View so that we can compute sourceBox.lineHeight = view.childNodes[0].clientHeight;
  1815.         var view = getChildByClass(sourceBox, 'sourceViewport');
  1816.         var max = scriptBlockSize;
  1817.         if (max > sourceBox.totalMax)
  1818.             max = sourceBox.totalMax;
  1819.         appendScriptLines(sourceBox, 1, max, view);
  1820.  
  1821.         delete this.lastScrollTop;
  1822.         setTimeout( bind(function delayScrollToLineOne()
  1823.         {
  1824.             var view = getChildByClass(sourceBox, 'sourceViewport');
  1825.             
  1826.             sourceBox.viewport = view;
  1827.             sourceBox.lineHeight = view.firstChild.clientHeight;  // sourceRow
  1828.             
  1829.             if (sourceBox.lineHeight == 0) // if the panel is not selected (not active), then the DOM is not correct anyway.
  1830.                 return;
  1831.  
  1832.             var sourceTextExample = view.firstChild.firstChild; // sourceLine
  1833.             if (sourceTextExample)
  1834.             {
  1835.                 var style = sourceTextExample.ownerDocument.defaultView.getComputedStyle(sourceTextExample, "");
  1836.                 var box = getBoxFromStyles(style, sourceTextExample);
  1837.                 sourceBox.lineNoWidth = box.width; // sourceLine width
  1838.             }
  1839.             else 
  1840.                 FBTrace.sysout("view?", view);
  1841.  
  1842.             if (sourceBox.scrollTop != 0)
  1843.                 sourceBox.scrollTop = 0; // causes onscroll event that triggers first buildViewAround
  1844.             else
  1845.                 this.reView(sourceBox);
  1846.         }, this));
  1847.  
  1848.         if (sourceFile.href)
  1849.             this.sourceBoxes[sourceFile.href] = sourceBox;
  1850.         else
  1851.             this.anonSourceBoxes.push(sourceBox);
  1852.  
  1853.         return sourceBox;
  1854.     },
  1855.  
  1856.     getSourceBoxBySourceFile: function(sourceFile)
  1857.     {
  1858.         if (sourceFile.href)
  1859.         {
  1860.             var sourceBox = this.getSourceBoxByURL(sourceFile.href);
  1861.             if (sourceBox && sourceBox.repObject == sourceFile)
  1862.                 return sourceBox;
  1863.             else
  1864.                 return null;  // cause a new one to be created
  1865.         }
  1866.  
  1867.         for (var i = 0; i < this.anonSourceBoxes.length; ++i)
  1868.         {
  1869.             var sourceBox = this.anonSourceBoxes[i];
  1870.             if (sourceBox.repObject == sourceFile)
  1871.                 return sourceBox;
  1872.         }
  1873.     },
  1874.  
  1875.     getSourceBoxByURL: function(url)
  1876.     {
  1877.         // if this.sourceBoxes is undefined, you need to call initializeSourceBoxes in your panel.initialize()
  1878.         return url ? this.sourceBoxes[url] : null;
  1879.     },
  1880.  
  1881.     showSourceFile: function(sourceFile)
  1882.     {
  1883.         var sourceBox = this.getSourceBoxBySourceFile(sourceFile);
  1884.         if (!sourceBox)
  1885.         {
  1886.             sourceBox = this.createSourceBox(sourceFile, this.getDecorator());
  1887.             this.panelNode.appendChild(sourceBox);
  1888.         }
  1889.  
  1890.         this.showSourceBox(sourceBox);
  1891.     },
  1892.  
  1893.     getSourceLink: function(lineNo)
  1894.     {
  1895.         return new SourceLink(this.selectedSourceBox.repObject.href, lineNo, this.getSourceType());
  1896.     },
  1897.  
  1898.     scrollToLine: function(href, lineNo, highlighter)
  1899.     {
  1900.         if (this.context.scrollTimeout)
  1901.         {
  1902.             this.context.clearTimeout(this.contextscrollTimeout);
  1903.             delete this.context.scrollTimeout
  1904.         }
  1905.  
  1906.         this.context.scrollTimeout = this.context.setTimeout(bindFixed(function()
  1907.         {
  1908.             // At this time we know which sourcebox is selected but the viewport is not selected.
  1909.             // We need to scroll, let the scroll handler set the viewport, then highlight any lines visible.
  1910.             if (this.selectedSourceBox)
  1911.             {
  1912.                 if (highlighter) this.selectedSourceBox.highlighter = highlighter;
  1913.                 var halfViewableLines = this.selectedSourceBox.halfViewableLines ? this.selectedSourceBox.halfViewableLines : 10;
  1914.                 this.selectedSourceBox.scrollTop = (lineNo - halfViewableLines) * this.selectedSourceBox.lineHeight;
  1915.  
  1916.                 this.applyDecorator(this.selectedSourceBox); // scroll event may already call this
  1917.  
  1918.                 if (uiListeners.length > 0)
  1919.                 {
  1920.                     var link = new SourceLink(this.selectedSourceBox.repObject.href, lineNo, this.getSourceType());
  1921.                     dispatch(uiListeners, "onLineSelect", [link]);
  1922.                 }
  1923.             }
  1924.         }, this));
  1925.     },
  1926.  
  1927.     jumpHighlightFactory: function(lineNo, context)
  1928.     {
  1929.         return function jumpHighlightIfInView(sourceBox)
  1930.         {
  1931.             var  lineNode = sourceBox.getLineNode(lineNo);
  1932.             if (lineNode)
  1933.             {
  1934.                 setClassTimed(lineNode, "jumpHighlight", context);
  1935.             }
  1936.             return false; // not sticky
  1937.         }
  1938.     },
  1939.  
  1940.     // should only be called onScroll
  1941.     buildViewAround: function(sourceBox, lineNo)  // defaults to first viewable lines
  1942.     {
  1943.         // TODO move the setup stuff to a resize event handler to make scrolling crisp
  1944.         var view = sourceBox.viewport;
  1945.         if (!view)
  1946.         {
  1947.             return;
  1948.         }
  1949.  
  1950.         var panelHeight = this.panelNode.clientHeight;
  1951.         //sourceBox.lineHeight = view.childNodes[0].clientHeight;
  1952.         var viewableLines = Math.round(panelHeight / sourceBox.lineHeight);  // eg 17
  1953.  
  1954.         var halfViewableLines = Math.round(viewableLines/2.0);  //eg 8
  1955.         sourceBox.halfViewableLines = halfViewableLines;
  1956.  
  1957.         var topLine = 1; // will be view.firstChild
  1958.         if (lineNo)
  1959.             topLine = lineNo - halfViewableLines;  // eg 2544 - 8
  1960.  
  1961.         if (topLine < 1)  // the lineNo was less than half the viewable lines, eg 4-8 = -4
  1962.             topLine = 1;
  1963.  
  1964.         var bottomLine = topLine + viewableLines;  // eg 2544 - 8 + 17
  1965.         if (bottomLine > sourceBox.totalMax)
  1966.         {
  1967.             bottomLine = sourceBox.totalMax;
  1968.             topLine = bottomLine - viewableLines;
  1969.             if (topLine < 1)
  1970.                 topLine = 1;
  1971.         }
  1972.  
  1973.         // Zero-based childNode index in view for lineNo. 2544 - (2544 - 8) = 8 or 4 - 1 = 3
  1974.         var centralLineNumber = lineNo ? (lineNo - topLine) : -1;
  1975.  
  1976.         clearNode(view);
  1977.  
  1978.         // Set the size on the line number field so the padding is filled with same style as source lines.
  1979.         var newScrollTop = (topLine - 1) * sourceBox.lineHeight;
  1980.         view.previousSibling.style.height = newScrollTop + "px";
  1981.         view.nextSibling.style.height = (sourceBox.totalMax - bottomLine) * sourceBox.lineHeight + "px";
  1982.         
  1983.         //sourceRow
  1984.         view.previousSibling.firstChild.style.height = newScrollTop + "px";
  1985.         view.nextSibling.firstChild.style.height = (sourceBox.totalMax - bottomLine) * sourceBox.lineHeight + "px";
  1986.         
  1987.         //sourceLine
  1988.         view.previousSibling.firstChild.firstChild.style.height = newScrollTop + "px";
  1989.         view.nextSibling.firstChild.firstChild.style.height = (sourceBox.totalMax - bottomLine) * sourceBox.lineHeight + "px";
  1990.         
  1991.         view.previousSibling.firstChild.firstChild.style.width = sourceBox.lineNoWidth + "px";
  1992.         view.nextSibling.firstChild.firstChild.style.width = sourceBox.lineNoWidth +"px";
  1993.  
  1994.         sourceBox.firstViewableLine = topLine;
  1995.         sourceBox.lastViewableLine = bottomLine;
  1996.  
  1997.         appendScriptLines(sourceBox, topLine, bottomLine, view);
  1998.  
  1999.         this.lastScrollTop = sourceBox.scrollTop;  // prevent reView before sourceBoxDecoratorTimeout reset scrollTop
  2000.  
  2001.         this.applyDecorator(sourceBox);
  2002.  
  2003.         return;
  2004.     },
  2005.  
  2006.     applyDecorator: function(sourceBox)
  2007.     {
  2008.         if (this.context.sourceBoxDecoratorTimeout)
  2009.         {
  2010.             this.context.clearTimeout(this.context.sourceBoxDecoratorTimeout);
  2011.             delete this.context.sourceBoxDecoratorTimeout;
  2012.         }
  2013.         this.context.sourceBoxDecoratorTimeout = this.context.setTimeout(bindFixed(function delaySourceBoxDecorator()
  2014.         {
  2015.             try
  2016.             {
  2017.                 if (sourceBox.highlighter)
  2018.                 {
  2019.                     var sticky = sourceBox.highlighter(sourceBox);
  2020.                     if (!sticky)
  2021.                         delete sourceBox.highlighter;
  2022.                 }
  2023.                 sourceBox.decorator(sourceBox, sourceBox.repObject);
  2024.  
  2025.                 if (uiListeners.length > 0) dispatch(uiListeners, "onApplyDecorator", [sourceBox]);
  2026.             }
  2027.             catch (exc)
  2028.             {
  2029.             }
  2030.         }, this));
  2031.     },
  2032.  
  2033.     reView: function(sourceBox)  // called for all scroll events, including any time sourcebox.scrollTop is set
  2034.     {
  2035.         var scrollTop = sourceBox.scrollTop;
  2036.  
  2037.         //if (scrollTop == this.lastScrollTop)
  2038.         //    return;
  2039.  
  2040.         if (!this.lastScrollTop)
  2041.             this.lastScrollTop = 0;
  2042.  
  2043.         var scrollStep = sourceBox.lineHeight;
  2044.         if (!scrollStep || scrollStep < 1)
  2045.         {
  2046.             this.buildViewAround(sourceBox, 1);
  2047.         }
  2048.         else
  2049.         {
  2050.             var newTopLine = Math.round(scrollTop/scrollStep + 1);
  2051.             var newBottomLine = Math.round((scrollTop + sourceBox.clientHeight)/scrollStep);
  2052.  
  2053.             var newCenterLine = Math.round((newTopLine + newBottomLine)/2.0);
  2054.  
  2055.             this.buildViewAround(sourceBox, newCenterLine);
  2056.         }
  2057.  
  2058.         this.lastScrollTop = scrollTop;
  2059.     },
  2060.  
  2061.     onResize: function(event)   
  2062.     {
  2063.         // The resize target is Firebug as a whole. But most of the UI needs no special code for resize.
  2064.         // But our SourceBoxPanel has viewport that will change size.
  2065.         if (this.selectedSourceBox)
  2066.             this.reView(this.selectedSourceBox);
  2067.     },
  2068.     
  2069. });
  2070.  
  2071. function loadScriptLines(sourceFile, context)  // array of lines
  2072. {
  2073.     if (sourceFile.source)
  2074.         return sourceFile.source;
  2075.     else
  2076.         return context.sourceCache.load(sourceFile.href);
  2077. }
  2078.  
  2079. function appendScriptLines(sourceBox, min, max, panelNode)
  2080. {
  2081.     var html = getSourceLineRange(sourceBox, min, max);
  2082.     appendInnerHTML(panelNode, html);
  2083. }
  2084.  
  2085. function getLineNodeIfViewable(lineNo)
  2086. {
  2087.     if (lineNo >= this.firstViewableLine && lineNo <= this.lastViewableLine)
  2088.     {
  2089.         var view = getChildByClass(this, 'sourceViewport');
  2090.         return view.childNodes[lineNo - this.firstViewableLine];
  2091.     }
  2092.     return null;
  2093. }
  2094.  
  2095. function getSourceBoxLineAsHTML(lineNo)  // XXXjjb TODO make this a prototype
  2096. {
  2097.     return escapeHTML(this.lines[lineNo]);
  2098. };
  2099.  
  2100.  
  2101. // ************************************************************************************************
  2102.  
  2103. Firebug.Rep = domplate(
  2104. {
  2105.     className: "",
  2106.     inspectable: true,
  2107.  
  2108.     supportsObject: function(object, type)
  2109.     {
  2110.         return false;
  2111.     },
  2112.  
  2113.     inspectObject: function(object, context)
  2114.     {
  2115.         context.chrome.select(object);
  2116.     },
  2117.  
  2118.     browseObject: function(object, context)
  2119.     {
  2120.     },
  2121.  
  2122.     persistObject: function(object, context)
  2123.     {
  2124.     },
  2125.  
  2126.     getRealObject: function(object, context)
  2127.     {
  2128.         return object;
  2129.     },
  2130.  
  2131.     getTitle: function(object)
  2132.     {
  2133.         var label = safeToString(object);
  2134.  
  2135.         var re = /\[object (.*?)\]/;
  2136.         var m = re.exec(label);
  2137.         return m ? m[1] : label;
  2138.     },
  2139.  
  2140.     getTooltip: function(object)
  2141.     {
  2142.         return null;
  2143.     },
  2144.  
  2145.     getContextMenuItems: function(object, target, context)
  2146.     {
  2147.         return [];
  2148.     },
  2149.  
  2150.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2151.     // Convenience for domplates
  2152.  
  2153.     STR: function(name)
  2154.     {
  2155.         return $STR(name);
  2156.     },
  2157.  
  2158.     cropString: function(text)
  2159.     {
  2160.         return cropString(text);
  2161.     },
  2162.  
  2163.     toLowerCase: function(text)
  2164.     {
  2165.         return text.toLowerCase();
  2166.     },
  2167.  
  2168.     plural: function(n)
  2169.     {
  2170.         return n == 1 ? "" : "s";
  2171.     }
  2172. });
  2173.  
  2174. // ************************************************************************************************
  2175.  
  2176. Firebug.ActivableModule = extend(Firebug.Module,
  2177. {
  2178.     panelName: null,
  2179.     panelBar1: $("fbPanelBar1"),
  2180.     activeContexts: null,
  2181.  
  2182.     initialize: function()
  2183.     {
  2184.         this.activeContexts = [];
  2185.     },
  2186.  
  2187.     initializeUI: function(detachArgs)
  2188.     {
  2189.         this.updateTab(null);
  2190.     },
  2191.  
  2192.     initContext: function(context)
  2193.     {
  2194.         // Add observers for permissions and preference changes so, activable modules
  2195.         // (net, script) can be properly updated.
  2196.         observerService.addObserver(this, "perm-changed", false);
  2197.         prefs.addObserver(this.getPrefDomain(), this, false);
  2198.  
  2199.         var persistedPanelState = this.syncPersistedPanelState(context, true);
  2200.     },
  2201.  
  2202.     syncPersistedPanelState: function(context, beginOrEnd)
  2203.     {
  2204.         var persistedPanelState = getPersistedState(context, this.panelName);
  2205.  
  2206.         persistedPanelState.enabled = this.isHostEnabled(context);
  2207.  
  2208.         if (persistedPanelState.enabled)
  2209.             this.panelActivate(context, beginOrEnd);
  2210.         else
  2211.             this.panelDeactivate(context, beginOrEnd);
  2212.  
  2213.         return persistedPanelState;
  2214.     },
  2215.  
  2216.     reattachContext: function(browser, context)
  2217.     {
  2218.         this.updateTab(context);
  2219.         var persistedPanelState = this.syncPersistedPanelState(context, false);
  2220.     },
  2221.  
  2222.     showContext: function(browser, context)
  2223.     {
  2224.         this.updateTab(context); /// 1.3.1 regression from 1.2.1, need context
  2225.     },
  2226.  
  2227.     destroyContext: function(context)
  2228.     {
  2229.         observerService.removeObserver(this, "perm-changed");
  2230.         prefs.removeObserver(this.getPrefDomain(), this);
  2231.  
  2232.         this.panelDeactivate(context, true);
  2233.     },
  2234.  
  2235.     panelActivate: function(context, init)
  2236.     {
  2237.         if (Firebug.getSuspended())
  2238.             Firebug.resume();  // This will cause onResumeFirebug for every context including this one.
  2239.  
  2240.         if (this.isEnabled(context))
  2241.             return;
  2242.  
  2243.         if (this.activeContexts.length == 0)
  2244.             this.onFirstPanelActivate(context, init);
  2245.  
  2246.         this.activeContexts.push(context);
  2247.         
  2248.         var panel = context.getPanel(this.panelName, true);
  2249.         if (panel)
  2250.             panel.enablePanel();
  2251.  
  2252.         dispatch(modules, "onPanelActivate", [context, init, this.panelName]);
  2253.         
  2254.         this.resetTooltip();  // 1.3.1 set tooltip after modules look at state
  2255.     },
  2256.  
  2257.     panelDeactivate: function(context, destroy)
  2258.     {
  2259.         if (!this.isEnabled(context))
  2260.             return;
  2261.  
  2262.         var i = this.activeContexts.indexOf(context);
  2263.         if (i != -1)
  2264.             this.activeContexts.splice(i, 1);
  2265.         else
  2266.         {
  2267.             return;
  2268.         }
  2269.  
  2270.         dispatch(modules, "onPanelDeactivate", [context, destroy, this.panelName]);
  2271.  
  2272.         if (!destroy)
  2273.         {
  2274.             var panel = context.getPanel(this.panelName, true);
  2275.             if (panel)  // else? xxxJJB vs 1.2
  2276.                 panel.disablePanel();
  2277.  
  2278.             var chrome = context ? context.chrome : FirebugChrome;
  2279.             var panelBar1 = chrome.$("fbPanelBar1");
  2280.  
  2281.             // Refresh the panel only if it's currently selected.
  2282.             if (panel && panelBar1.selectedPanel == panel)
  2283.             {
  2284.                 var state = Firebug.getPanelState(panel);
  2285.                 panel.show(state);
  2286.             }
  2287.         }
  2288.  
  2289.         if (this.activeContexts.length == 0)
  2290.             this.onLastPanelDeactivate(context, destroy);
  2291.         
  2292.         this.resetTooltip();  // 1.3.1 set tooltip after modules process changes
  2293.     },
  2294.  
  2295.     resetTooltip: function()
  2296.     {
  2297.         var tooltip = "Firebug "+ Firebug.getVersion();
  2298.         
  2299.         if (fbStatusIcon.getAttribute("errors") == "on")
  2300.             tooltip +=" console: on,";
  2301.         else
  2302.             tooltip +=" console: off,";
  2303.             
  2304.         if (fbStatusIcon.getAttribute("net") == "on")
  2305.             tooltip +=" net: on,";
  2306.         else
  2307.             tooltip +=" net: off,";
  2308.         
  2309.         if (fbStatusIcon.getAttribute("jsd") == "on")
  2310.             tooltip +=" script: on";
  2311.         else
  2312.             tooltip +=" script: off,";
  2313.         
  2314.         if (Firebug.getSuspended())
  2315.             tooltip += ": " + Firebug.getSuspended();
  2316.         else
  2317.         {
  2318.             var urls = Firebug.ActivableModule.getURLsForAllActiveContexts();
  2319.             if (urls.length > 0)
  2320.             {
  2321.                 tooltip += " activated by "+urls.length+" page(s)";
  2322.                 for (var i = 0; i < urls.length; i++)
  2323.                 {
  2324.                     try {
  2325.                         tooltip += "\n"+decodeURI(urls[i]); 
  2326.                     } catch (e) {
  2327.                         // xxxHonza: from some reason FBTrace is undefined here.
  2328.                         dump("Firebug.ActivableModule.resetTooltip EXCEPTION " + e + "\n");
  2329.                     }
  2330.                 }
  2331.             }
  2332.         }
  2333.         fbStatusIcon.setAttribute("tooltiptext", tooltip);
  2334.     },
  2335.  
  2336.     getURLsForAllActiveContexts: function()
  2337.     {
  2338.         var contextURLSet = [];  // create a list of all unique activeContexts in all modules
  2339.         for (var i = 0; i < modules.length; i++)
  2340.         {
  2341.             var module = modules[i];
  2342.             if (module.activeContexts)
  2343.             {
  2344.                 for (var ic = 0; ic < module.activeContexts.length; ic++)
  2345.                 {
  2346.                     try
  2347.                     {
  2348.                         var cw = module.activeContexts[ic].window;
  2349.                         if (cw.location && cw.location.toString)
  2350.                         {
  2351.                             var url = cw.location.toString();
  2352.                             if (contextURLSet.indexOf(url) == -1)
  2353.                                 contextURLSet.push(url);
  2354.                         }
  2355.                     }
  2356.                     catch(e)
  2357.                     {
  2358.                     }
  2359.                 }
  2360.             }
  2361.         }
  2362.         return contextURLSet;
  2363.     },
  2364.     // ---------------------------------------------------------------------------------------
  2365.  
  2366.     onFirstPanelActivate: function(context, init)
  2367.     {
  2368.         // Just before onPanelActivate, no previous activecontext
  2369.     },
  2370.  
  2371.     onPanelActivate: function(context, init, panelName)
  2372.     {
  2373.         // Module activation code. Just added to activeContexts
  2374.     },
  2375.  
  2376.     onPanelDeactivate: function(context, destroy, panelName)
  2377.     {
  2378.         // Module deactivation code. Just removed from activeContexts
  2379.     },
  2380.  
  2381.     onLastPanelDeactivate: function(context, init)
  2382.     {
  2383.         // Just after onPanelDeactivate, no remaining activecontext
  2384.     },
  2385.  
  2386.     onSuspendFirebug: function(context)
  2387.     {
  2388.         // When the user requests Suspend Firebug. Modules should remove listeners, disable function that takes resources
  2389.     },
  2390.  
  2391.     onResumeFirebug: function(context)
  2392.     {
  2393.         // When the user requests Resume Firebug Modules should undo the work done in onSuspendFirebug
  2394.     },
  2395.     // ---------------------------------------------------------------------------------------
  2396.  
  2397.     isEnabled: function(context)
  2398.     {
  2399.         if (!context)
  2400.             return false;
  2401.  
  2402.         return (this.activeContexts.indexOf(context) != -1);
  2403.     },
  2404.  
  2405.     wasEnabled: function(context)
  2406.     {
  2407.         if (!context)
  2408.             return false;
  2409.  
  2410.         var persistedPanelState = getPersistedState(context, this.panelName);
  2411.         if (persistedPanelState)
  2412.             return persistedPanelState.enabled;
  2413.  
  2414.         return false;
  2415.     },
  2416.  
  2417.     getPrefDomain: function()
  2418.     {
  2419.         if (!this.prefDomain)
  2420.             this.prefDomain = Firebug.prefDomain + "." + this.panelName;
  2421.  
  2422.         return this.prefDomain;
  2423.     },
  2424.  
  2425.     getHostForURI: function(browserURI)
  2426.     {
  2427.         if (Firebug.filterSystemURLs)
  2428.             return isSystemURL(browserURI.spec) ? "" : browserURI.host;
  2429.         else
  2430.         {
  2431.             if (browserURI.spec.substr(0, 6) == "about:")
  2432.                 return "";
  2433.             else
  2434.             {
  2435.                 try
  2436.                 {
  2437.                     return browserURI.host;
  2438.                 }
  2439.                 catch (exc)
  2440.                 {
  2441.                     return "";
  2442.                 }
  2443.             }
  2444.         }
  2445.     },
  2446.  
  2447.     /**
  2448.      * Returns true if the module can be enabled for the specified host.
  2449.      * Returns false otherwise.
  2450.      */
  2451.     isHostEnabled: function(context)
  2452.     {
  2453.         var option = this.getHostPermission(context);
  2454.         switch (option)
  2455.         {
  2456.             case "enable":
  2457.             case "enable-site":
  2458.                 return true;
  2459.  
  2460.             case "disable":
  2461.             case "disable-site":
  2462.                 return false;
  2463.         }
  2464.  
  2465.     },
  2466.  
  2467.     /**
  2468.      * Sets host permission for the module.
  2469.      * There are three types of permissions that can be specified in the option:
  2470.      * "enable" - the module should be enabled for all sites.
  2471.      * "disable" - the module should be disbled for all sites.
  2472.      * "enable-site" - the module should be enabled for this site.
  2473.      * "disable-site" - the module should be disabled for this site.
  2474.      */
  2475.     setHostPermission: function(context, option)
  2476.     {
  2477.         var browserURI = FirebugChrome.getBrowserURI(context);
  2478.         var prefDomain = this.getPrefDomain();
  2479.         var enable = (option.indexOf("enable") == 0) ? true : false;
  2480.         var global = (option.indexOf("site") == -1) ? true : false;
  2481.  
  2482.         if (!browserURI.spec || isSystemURL(browserURI.spec))
  2483.             Firebug.setPref(prefDomain, "enableSystemPages", (global ? "" : option));
  2484.         else if (isLocalURL(browserURI.spec))
  2485.             Firebug.setPref(prefDomain, "enableLocalFiles", (global ? "" : option));
  2486.         else if (isDataURL(browserURI.spec))
  2487.             return;
  2488.  
  2489.         if (!browserURI.spec || isSystemURL(browserURI.spec) || isLocalURL(browserURI.spec))
  2490.         {
  2491.             // If the global option is set while system or local page is displayed
  2492.             // not to forget to update the global preference.
  2493.             if (global)
  2494.                 Firebug.setPref(prefDomain, "enableSites", enable);
  2495.  
  2496.             return;
  2497.         }
  2498.  
  2499.         switch(option)
  2500.         {
  2501.             case "enable-site":
  2502.                 permissionManager.add(browserURI, prefDomain, permissionManager.ALLOW_ACTION);
  2503.                 break;
  2504.  
  2505.             case "disable-site":
  2506.                 permissionManager.add(browserURI, prefDomain, permissionManager.DENY_ACTION);
  2507.                 break;
  2508.  
  2509.             default:
  2510.                 permissionManager.remove(browserURI.host, prefDomain);
  2511.                 Firebug.setPref(prefDomain, "enableSites", enable);
  2512.                 break;
  2513.         }
  2514.     },
  2515.  
  2516.     /*
  2517.      * Returns current host permision for the module.
  2518.      * Return value: "enable", "disable", "enable-site" or "disable-site".
  2519.      */
  2520.     getHostPermission: function(context)
  2521.     {
  2522.         var browserURI = FirebugChrome.getBrowserURI(context);
  2523.         var prefDomain = this.getPrefDomain();
  2524.  
  2525.         // If it's a local-file or a system-page see preferences.
  2526.         // In case of eg resource://gre/res/hiddenWindow.html the spec can be null.
  2527.         if (!browserURI || !browserURI.spec || isSystemURL(browserURI.spec))
  2528.         {
  2529.             var option = Firebug.getPref(prefDomain, "enableSystemPages");
  2530.  
  2531.             // If the preference isn't set use the global option.
  2532.             return option ? option : (this.isAlwaysEnabled() ? "enable" : "disable");
  2533.         }
  2534.         else if (isLocalURL(browserURI.spec))
  2535.         {
  2536.             var option = Firebug.getPref(prefDomain, "enableLocalFiles");
  2537.             return option ? option : (this.isAlwaysEnabled() ? "enable" : "disable");
  2538.         }
  2539.         else if (isDataURL(browserURI.spec))
  2540.         {
  2541.             return "enable-site";
  2542.         }
  2543.  
  2544.         switch (permissionManager.testPermission(browserURI, prefDomain))
  2545.         {
  2546.             case nsIPermissionManager.ALLOW_ACTION:
  2547.                 return "enable-site";
  2548.             case nsIPermissionManager.DENY_ACTION:
  2549.                 return "disable-site";
  2550.  
  2551.             default:
  2552.                 return this.isAlwaysEnabled() ? "enable" : "disable";
  2553.         }
  2554.     },
  2555.  
  2556.     /**
  2557.      * Return true if the module should be enabled by default.
  2558.      */
  2559.     isAlwaysEnabled: function()
  2560.     {
  2561.         var prefDomain = this.getPrefDomain();
  2562.         return Firebug.getPref(prefDomain, "enableSites");
  2563.     },
  2564.  
  2565.     /**
  2566.      * Opens a dialog with list of created permissions for this module.
  2567.      */
  2568.     openPermissions: function(event, context)
  2569.     {
  2570.         cancelEvent(event);
  2571.  
  2572.         var browserURI = FirebugChrome.getBrowserURI(context);
  2573.         var host = this.getHostForURI(browserURI);
  2574.  
  2575.         var params = {
  2576.             permissionType: this.getPrefDomain(),
  2577.             windowTitle: $STR(this.panelName + ".Permissions"),
  2578.             introText: $STR(this.panelName + ".PermissionsIntro"),
  2579.             blockVisible: true,
  2580.             sessionVisible: false,
  2581.             allowVisible: true,
  2582.             prefilledHost: host,
  2583.         };
  2584.  
  2585.         openWindow("Browser:Permissions", "chrome://browser/content/preferences/permissions.xul",
  2586.             "", params);
  2587.     },
  2588.  
  2589.     observe: function(subject, topic, data)
  2590.     {
  2591.         try
  2592.         {
  2593.             // This methods observes two events:
  2594.             // perm-changed - fired when permissions are changed.
  2595.             // nsPref:changed - fired when preferences are changed.
  2596.             if (topic == 'perm-changed')
  2597.             {
  2598.                 if (subject instanceof Ci.nsIPermission)
  2599.                 {
  2600.                     var host = subject.host;
  2601.                     var prefDomain = subject.type;  // eg extensions.firebug.script
  2602.                     dispatch(modules, "activationChange", [host, prefDomain, data]); // data will be 'added' or 'deleted'
  2603.                 }
  2604.                 else
  2605.                 {
  2606.                 }
  2607.             }
  2608.             else if (topic == "nsPref:changed")
  2609.             {
  2610.                 var prefDomain = this.getPrefDomain();
  2611.                 if (data == prefDomain + ".enableLocalFiles" ||
  2612.                     data == prefDomain + ".enableSystemPages" ||
  2613.                     data == prefDomain + ".enableSites")
  2614.                 {
  2615.                     dispatch(modules, "activationChange", [null, prefDomain, data]);
  2616.                 }
  2617.             }
  2618.         }
  2619.         catch (exc)
  2620.         {
  2621.         }
  2622.     },
  2623.  
  2624.     activationChange: function(host, prefDomain, data)
  2625.     {
  2626.         if (prefDomain == this.getPrefDomain())
  2627.         {
  2628.             var module = this;
  2629.             TabWatcher.iterateContexts(
  2630.                 function changeActivation(context)
  2631.                 {
  2632.                     try
  2633.                     {
  2634.                         var location = context.window.location;
  2635.  
  2636.                         if (isLocalURL(location.href) || isSystemURL(location.href))
  2637.                             module.syncPersistedPanelState(context, false);
  2638.                         else if (location.host.indexOf(host) != -1)
  2639.                             module.syncPersistedPanelState(context, false);
  2640.                         else if ((prefDomain + ".enableSites") == data)
  2641.                             module.syncPersistedPanelState(context, false);
  2642.                     }
  2643.                     catch (exc)
  2644.                     {
  2645.                     }
  2646.                 }
  2647.             );
  2648.         }
  2649.     },
  2650.  
  2651.     getMenuLabel: function(option, location)
  2652.     {
  2653.         var label = "";
  2654.         var host = "";
  2655.  
  2656.         switch (option)
  2657.         {
  2658.         case "disable-site":
  2659.             if (isSystemURL(location.spec))
  2660.                 label = "SystemPagesDisable";
  2661.             else if (!getURIHost(location))
  2662.                 label = "LocalFilesDisable";
  2663.             else
  2664.                 label = "HostDisable";
  2665.             break;
  2666.  
  2667.         case "enable-site":
  2668.             if (isSystemURL(location.spec))
  2669.                 label = "SystemPagesEnable";
  2670.             else if (!getURIHost(location))
  2671.                 label = "LocalFilesEnable";
  2672.             else
  2673.                 label = "HostEnable";
  2674.             break;
  2675.  
  2676.         case "enable":
  2677.             return $STR("panel.Enabled");
  2678.  
  2679.         case "disable":
  2680.             return $STR("panel.Disabled");
  2681.         }
  2682.  
  2683.         if (!label)
  2684.             return null;
  2685.  
  2686.         label = this.panelName + "." + label;
  2687.         return $STRF(label, [getURIHost(location)]);
  2688.     },
  2689.  
  2690.     updateTab: function(context)
  2691.     {
  2692.         var chrome = context ? context.chrome : FirebugChrome;
  2693.         var panelBar = chrome.$("fbPanelBar1");
  2694.         var tab = panelBar.getTab(this.panelName);
  2695.  
  2696.         // Update activable tab menu.
  2697.         tab.initTabMenu(this);
  2698.  
  2699.         // Update tab label.
  2700.         if (context)
  2701.         {
  2702.             var enabled = this.isEnabled(context);
  2703.             tab.setAttribute("disabled", enabled ? "false" : "true");
  2704.         }
  2705.     }
  2706. });
  2707.  
  2708. // ************************************************************************************************
  2709.  
  2710. Firebug.ModuleManagerPage = domplate(Firebug.Rep,
  2711. {
  2712.     tag:
  2713.         DIV({class: "moduleManagerBox"},
  2714.             H1({class: "moduleManagerHead"},
  2715.                 SPAN("$pageTitle")
  2716.             ),
  2717.             P({class: "moduleManagerDescription"},
  2718.                 $STR("moduleManager.description")
  2719.             ),
  2720.             TABLE({class: "activableModuleTable", cellpadding: 0, cellspacing: 0},
  2721.                 TBODY(
  2722.                     FOR("module", "$modules",
  2723.                         TR({class: "activableModuleRow", _repObject: "$module",
  2724.                             $panelDisabled: "$module|isModuleDisabled"},
  2725.                             TD({class: "activableModuleCell activableModuleState"},
  2726.                                 INPUT({class: "activableModuleCheckBox", type: "checkbox",
  2727.                                     onchange: "$onModuleStateChanged"})
  2728.                             ),
  2729.                             TD({class: "activableModuleCell activableModuleName",
  2730.                                 onclick: "$onModuleNameClick"}, "$module|getModuleName"),
  2731.                             TD({class: "activableModuleCell activableModuleDesc"}, "$module|getModuleDesc"),
  2732.                             TD({class: "activableModuleCell"}, "$module|getModuleState")
  2733.                         )
  2734.                     )
  2735.                 )
  2736.             ),
  2737.             DIV({class: "moduleManagerRow"},
  2738.                 BUTTON({class: "moduleManagerApplyButton", onclick: "$onEnable"},
  2739.                     SPAN("$enableHostLabel")
  2740.                 )
  2741.             )
  2742.          ),
  2743.  
  2744.     getModuleName: function(module)
  2745.     {
  2746.         var panelType = Firebug.getPanelType(module.panelName);
  2747.         return Firebug.getPanelTitle(panelType);
  2748.     },
  2749.  
  2750.     getModuleDesc: function(module)
  2751.     {
  2752.         return module.description;
  2753.     },
  2754.  
  2755.     getModuleState: function(module)
  2756.     {
  2757.         var currentURI = FirebugChrome.getBrowserURI(this.context);
  2758.         var hostURI = getURIHost(currentURI);
  2759.         var option = module.getHostPermission(this.context);
  2760.  
  2761.         switch (option) {
  2762.         case "disable-site":
  2763.             return $STRF("moduleManager.Disabled For Site", [hostURI]);
  2764.         case "enable-site":
  2765.             return $STRF("moduleManager.Enabled For Site", [hostURI]);
  2766.         case "enable":
  2767.             return $STR("moduleManager.Enabled Always");
  2768.         case "disable":
  2769.             return $STR("moduleManager.Disabled Always");
  2770.         }
  2771.     },
  2772.  
  2773.     isModuleDisabled: function(module)
  2774.     {
  2775.         return !module.isEnabled(this.context);
  2776.     },
  2777.  
  2778.     isModuleEnabled: function(module)
  2779.     {
  2780.         return module.isEnabled(this.context);
  2781.     },
  2782.  
  2783.     onModuleStateChanged: function(event)
  2784.     {
  2785.         this.updateApplyButton();
  2786.     },
  2787.  
  2788.     updateApplyButton: function()
  2789.     {
  2790.         var disabled = true;
  2791.         for (var i=0; i<this.inputs.length; i++)
  2792.         {
  2793.             var input = this.inputs[i];
  2794.             if (input.checked != input.originalValue)
  2795.             {
  2796.                 disabled = false;
  2797.                 break;
  2798.             }
  2799.         }
  2800.  
  2801.         this.applyButton.disabled = disabled;
  2802.     },
  2803.  
  2804.     onModuleNameClick: function(event)
  2805.     {
  2806.         var moduleRow = Firebug.getRepNode(event.target);
  2807.         var checkBox = getElementByClass(moduleRow, "activableModuleCheckBox");
  2808.         checkBox.checked = checkBox.checked ? false : true;
  2809.  
  2810.         this.updateApplyButton();
  2811.     },
  2812.  
  2813.     onEnable: function(event)
  2814.     {
  2815.         var needReload = false;
  2816.         for (var i=0; i<this.inputs.length; i++)
  2817.         {
  2818.             var input = this.inputs[i];
  2819.             if (!hasClass(input, "activableModuleCheckBox"))
  2820.                 continue;
  2821.  
  2822.             var model = Firebug.getRepObject(input);
  2823.             if (input.checked)
  2824.             {
  2825.                 var oneReload = this.enableModule(model);
  2826.                 needReload = needReload || oneReload;
  2827.             }
  2828.             else
  2829.                 this.disableModule(model);
  2830.  
  2831.             // Update panel's tab so, it reflects the current module state.
  2832.             model.updateTab(this.context);
  2833.         }
  2834.  
  2835.         this.refresh();
  2836.  
  2837.         if (needReload)
  2838.             this.context.window.location.reload();
  2839.     },
  2840.  
  2841.     enableModule: function(module)
  2842.     {
  2843.         if (!this.isModuleEnabled(module))
  2844.         {
  2845.             module.setHostPermission(this.context, "enable-site");
  2846.             return true;
  2847.         }
  2848.         return false;
  2849.     },
  2850.  
  2851.     disableModule: function(module)
  2852.     {
  2853.         if (this.isModuleEnabled(module))
  2854.             module.setHostPermission(this.context, "disable-site");
  2855.     },
  2856.  
  2857.     show: function(panel, module)
  2858.     {
  2859.         try
  2860.         {
  2861.             this.module = module;
  2862.             this.context = panel.context;
  2863.             this.panelNode = panel.panelNode;
  2864.             this.refresh();
  2865.         }
  2866.         catch(e)
  2867.         {
  2868.         }
  2869.     },
  2870.  
  2871.     hide: function(panel)
  2872.     {
  2873.         if (this.box)
  2874.             this.box.setAttribute("collapsed", "true");
  2875.     },
  2876.  
  2877.     refresh: function()
  2878.     {
  2879.         var currentURI = FirebugChrome.getBrowserURI(this.context);
  2880.         var hostURI = getURIHost(currentURI);
  2881.  
  2882.         if (isSystemURL(currentURI.spec))
  2883.             label = "moduleManager.systempages";
  2884.         else if (!hostURI)
  2885.             label = "moduleManager.localfiles";
  2886.  
  2887.         hostURI = hostURI ? hostURI : $STR(label);
  2888.  
  2889.         // Prepare arguments for the template (list of activableModules and
  2890.         // title for the apply button).
  2891.         var args = {
  2892.             modules: activableModules,
  2893.             pageTitle: $STRF("moduleManager.title", [this.getModuleName(this.module)]),
  2894.             enableHostLabel: $STRF("moduleManager.apply.title", [hostURI])
  2895.         };
  2896.  
  2897.         // Render panel HTML
  2898.         this.box = this.tag.replace(args, this.panelNode, this);
  2899.         this.panelNode.scrollTop = 0;
  2900.  
  2901.         // Update value of all checkboxes
  2902.         // xxxHonza: is there a better domplate way how to set default value for a checkbox?
  2903.         this.inputs = this.panelNode.getElementsByTagName("input");
  2904.         for (var i=0; i<this.inputs.length; i++)
  2905.         {
  2906.             var input = this.inputs[i];
  2907.             if (!hasClass(input, "activableModuleCheckBox"))
  2908.                 continue;
  2909.  
  2910.             // Set the checkboxes to cause the big button to return the panels to a state the user has liked.
  2911.             var module = Firebug.getRepObject(input);
  2912.             if (this.isModuleEnabled(module))
  2913.                 input.originalValue = true;
  2914.             else
  2915.                 input.originalValue = false;
  2916.  
  2917.             input.checked = input.originalValue;
  2918.         }
  2919.  
  2920.         this.applyButton = getElementByClass(this.panelNode, "moduleManagerApplyButton");
  2921.     }
  2922. });
  2923.  
  2924. // ************************************************************************************************
  2925.  
  2926.  
  2927. }});
  2928.